package com.sailor.excel.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.sailor.excel.httpclient.PostType;
import org.apache.http.Consts;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class HttpClientUtil {
    private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    private static final CloseableHttpAsyncClient httpClient;

    static {
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(3000)
                .setConnectionRequestTimeout(3000)
                .setSocketTimeout(3000)
                .build();
        httpClient = HttpAsyncClients.custom()
                .setDefaultRequestConfig(config)
                .build();
        httpClient.start();
    }

    public static <T> CompletableFuture<T> get(String url, TypeReference<T> tr, Map<String, String> headers, int timeout, String charset) {
        HttpGet request = new HttpGet(url);
        applyHeaders(request, headers);
        return sendAsync(request, tr, charset);
    }

    public static <T> CompletableFuture<T> post(String url, PostType postType, Object body, TypeReference<T> tr, Map<String, String> headers, int timeout, String charset) {
        HttpPost request = new HttpPost(url);
        applyHeaders(request, headers);

        if (postType == PostType.JSON) {
            request.setHeader("Content-Type", "application/json");
            String jsonStr = (body instanceof String) ? (String) body : JsonUtil.toString(body);
            request.setEntity(new StringEntity(jsonStr, Charset.forName(charset)));
        } else if (postType == PostType.X_WWW_FORM_URLENCODED) {
            Assert.isTrue(isMapOfStringString(body), "PostType = X_WWW_FORM_URLENCODED 时，PostBody类型应该为Map<String,String>");
            request.setHeader("Content-Type", "application/x-www-form-urlencoded");
            Map<String, String> map = (Map<String, String>) body;
            List<BasicNameValuePair> params = map.entrySet().stream()
                    .map(e -> new BasicNameValuePair(e.getKey(), e.getValue()))
                    .collect(Collectors.toList());
            request.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));
        }

        return sendAsync(request, tr, charset);
    }

    private static <T> CompletableFuture<T> sendAsync(HttpRequestBase request, TypeReference<T> tr, String charset) {
        CompletableFuture<T> future = new CompletableFuture<>();

        httpClient.execute(request, new FutureCallback<org.apache.http.HttpResponse>() {
            @Override
            public void completed(org.apache.http.HttpResponse response) {
                try {
                    int statusCode = response.getStatusLine().getStatusCode();
                    String body = EntityUtils.toString(response.getEntity(), charset);
                    if (statusCode == 200) {
                        future.complete(JsonUtil.toObject(body, tr));
                    } else {
                        logger.error("请求失败: {} 状态码: {}", request.getURI(), statusCode);
                        future.complete(null);
                    }
                } catch (Exception e) {
                    logger.error("处理响应失败", e);
                    future.completeExceptionally(e);
                }
            }

            @Override
            public void failed(Exception ex) {
                logger.error("请求失败: {}", request.getURI(), ex);
                future.completeExceptionally(ex);
            }

            @Override
            public void cancelled() {
                logger.warn("请求取消: {}", request.getURI());
                future.cancel(true);
            }
        });

        return future;
    }

    private static void applyHeaders(HttpRequestBase request, Map<String, String> headers) {
        if (headers != null) {
            headers.forEach(request::setHeader);
        }
    }

    private static boolean isMapOfStringString(Object obj) {
        if (obj instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) obj;
            if (!map.isEmpty()) {
                Object key = map.keySet().iterator().next();
                Object value = map.values().iterator().next();
                return key instanceof String && value instanceof String;
            }
        }
        return false;
    }
}
